/**
 * @file RetroreflectionServer.c
 * @author haiming
 * @brief 单行回射程序服务端
 * @version 0.1
 * @date 2022-07-19
 * 
 * @copyright Copyright (c) 2022
 * 
 */
#include "NetWorkProDefine.h"

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <net/if.h>
#include <pthread.h>

void RetroreflectionServ();
void ReroreflectionServ1(void);
void ReflectionSerTh(int argc, char** argv);
static void * doit(void *arg);

void str_echo(int sockfd)
{
    printf("str_echo :0x%x\n", sockfd);
    size_t n;
    char buf[MAXLEN] = {0};

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        n = read(sockfd, buf, MAXLEN);
        if(n > 0)
        {
            // printf("client :%s\n", buf);
            write(sockfd, buf, n);
            printf("server :%s\n", buf);
        }
        else if(n < 0 && errno == EINTR)
        {
            printf("continue to read\n");
            continue;
        }
        else
        {
            printf("unknown error, n :%ld\terrno :0x%x\n",n, errno);
            break;
        }
    }
}

void echo_serv_delay(int sockfd)
{
    printf("str_echo :0x%x\n", sockfd);
    size_t n;
    char buf[MAXLEN] = {0};

    while (1)
    {
        memset(buf, 0, sizeof(buf));
        n = read(sockfd, buf, MAXLEN);
        if(n > 0)
        {
            printf("client :%s\n", buf);
            sleep(10);
            write(sockfd, buf, n);
            printf("server :%s\n", buf);
        }
        else if(n < 0 && errno == EINTR)
        {
            printf("continue to read\n");
            continue;
        }
        else
        {
            printf("unknown error, n :%ld\terrno :0x%x\n",n, errno);
            break;
        }
    }
}
void ShowSockInfo(const struct sockaddr_in* sockaddr)
{
    printf("original addr :%0x\tport :%u\n", sockaddr->sin_addr.s_addr, sockaddr->sin_port);
    char ip[16] = {0};
    inet_ntop(AF_INET, &sockaddr->sin_addr.s_addr, ip, sizeof(ip));
    printf("afetr parsed, addr:%s\tport :%u\n", ip, ntohs(sockaddr->sin_port));
}

int main(int argc, char** argv)
{
    printf("Retroreflection Server: you are welcome!\n");
    if(argc < 2)
    {
        printf("usage : %s choice<1/2/3>\n", *argv);
        return -1;
    }

    if ('1' == *argv[1])
    {
        RetroreflectionServ();
    }
    else if('2' == *argv[1])
    {
        ReroreflectionServ1();
    }
    else if ('3' == *argv[1])
    {
        ReflectionSerTh(argc, argv);
    }
    else
    {
        printf("error argv[1] :%s", argv[1]);
    }
    printf("exit from main!\n");
    return 0;
}


void IPV42Str(unsigned int dwIP, char* szBuf, int len)
{
    printf("IPV42Str\n");
    memset(szBuf, 0, len);
    snprintf(szBuf, len, "%d.%d.%d.%d", dwIP&0xff,(dwIP>>8)&0xff,(dwIP>>16)&0xff,(dwIP>>24)&0xff);
    printf("buf :%s\n", szBuf);
}

void ReroreflectionServ1()
{
    printf("v1 run\n");
    int listenfd=-1, connfd=-1;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;
    
    bzero(&servaddr, sizeof(servaddr));
    unsigned int IP = get_local_ip("enp2s0");
    //GetIPByEthName("enp2s0", &IP);
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = IP;//htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    listenfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    bind(listenfd, (SA*)&servaddr, sizeof(servaddr));//绑定
	listen(listenfd, LISTENENQ);//监听

    clilen = sizeof(cliaddr);
    for(;;)
    {
        connfd = accept(listenfd, (SA*) &cliaddr, &clilen);
        ShowSockInfo(&cliaddr);
        if((childpid = fork()) == 0)
        {
            close(listenfd);
            str_echo(connfd);
            exit(0);
            //子进程僵死，直到父进程退出系统才会清理僵尸子进程
        }
        close(connfd);
    }
}
void RetroreflectionServ()
{
    printf("v run\n");
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    void sig_chld(int);
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(bind(listenfd, (SA*)&servaddr, sizeof(servaddr)) != 0 
        || listen(listenfd, LISTENENQ) != 0)
    {
        printf("start server error!\n");
        return ;
    }
    signal(SIGCHLD, sig_chld);
    clilen = sizeof(cliaddr);
    for (; ; )
    {
        if((connfd = accept(listenfd, (SA*)&cliaddr, &clilen)) < 0)
        {
            if(errno != EINTR)
            {
                printf("accept error\n");
                exit(0);
            }
            else
            {
                continue;
            }
        }
        if((childpid = fork()) == 0)
        {
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        close(connfd);        
    }
}

void sig_chld(int signo)
{
    pid_t pid;
    int stat;

    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    {
        printf("child %d terminated\n", pid);
    }
}



void ReflectionSerTh(int argc, char** argv)
{
    int listenfd, connfd;
    
    pthread_t tid;
    socklen_t addrlen, cliaddrlen;
    struct sockaddr *cliaddr;
    if(argc == 2)
    {
        char szport[8] = {0};
        snprintf(szport, sizeof(szport), "%d", SERV_PORT);
        listenfd = tcp_listen(NULL, szport, &addrlen);
    }
    else if(argc == 4)
    {
        listenfd = tcp_listen(argv[2], argv[3], &addrlen);
    }
    else
    {
        printf("usage : %s 3 [host] [service or port]\n", *argv);
    }
    
    cliaddrlen = sizeof(struct sockaddr);
    cliaddr = (struct sockaddr*)malloc(cliaddrlen);
    bzero(cliaddr, cliaddrlen);
    for(;listenfd > 0;)
    {
        connfd = accept(listenfd, cliaddr, &cliaddrlen);
        if (connfd > 0)
        {
            pthread_create(&tid, NULL, &doit, (void*)&connfd);
        }
        else
        {
            if(errno != EINTR)
            {
                printf("accept error, errno :%d\tmsg :%s\n", errno, strerror(errno));
                exit(0);
            }
            else
            {
                continue;
            }
        }
    }
}


static void * doit(void *arg)
{
    pthread_detach(pthread_self());
    str_echo(*(int*)arg);
    close(*(int*)arg);
    printf("close socket!\n");
    return NULL;
}